#include "p1.h"

string file;
ofstream output;
ofstream performanceOutput;
bool tiempo = false;

ostream& operator<<(ostream& os, const triplet& t){
	os << "<" << t.numero << ", " << t.carga << ", " << t.produccion << ">";
	return os;
}

string get_file_contents(const char *filename){
	ifstream in(filename, std::ios::in | std::ios::binary);
	if (in){
		string contents;
		in.seekg(0, std::ios::end);
		contents.resize(in.tellg());
		in.seekg(0, std::ios::beg);
		in.read(&contents[0], contents.size());
		in.close();
		return(contents);
	}
	perror("Error:");
	return 0; 
}

int main(int argc, char *argv[]) {
	
	if(argc ==1){
		cout << "Usando p1.in y p1.out" << endl;
		file = get_file_contents("p1.in");
		output.open("p1.out");
		if(output.fail()){
			cerr << "Error al abrir archivo de salida" << endl;
		}
	}

	else if(argc ==3){
		cout << "Usando " << argv[1] << " y " << argv[2] << endl;
		file = get_file_contents(argv[1]);
		output.open(argv[2]);
		if(output.fail()){
			cerr << "Error al abrir archivo de salida" << endl;
		}
	}

	else if(argc ==2 && string (argv[1]) == "time"){
		cout << "Calculando tiempos usando p1.in y p1.out con " << ITER << " iteraciones" << endl;
		tiempo = true;
		file = get_file_contents("p1.in");
		output.open("p1.out");
		if(output.fail())
			cerr << "Error al abrir archivo de salida" << endl;
		performanceOutput.open("p1.perf");
		if(performanceOutput.fail())
			cerr << "Error al abrir archivo de salida de performance" << endl;
		
	}
	
	else if (argc == 2 && string (argv[1]) == "generateInstances"){
	
		cout << "Generando instancias..." << endl;
		
		generarInstancias();
		
		cout << "Instancias generadas exitosamente." << endl; 
		
		return 0;	
	}
	

	else{
		cout << "Modo de uso: \"p1 input output\" o \"p1 time\" para benchmark" << endl;
		return 0;
    	}

	istringstream input;
	input.str(file);

	while(!input.eof()){
		if(input.peek() == '#'){
			break;
		}
		vector<triplet> p = cargar(input);
		resolver(p);
		salida(output,p);
	}
	output.close();
	if (tiempo)
		performanceOutput.close();
		
	return 0;
}

void generarInstancias(){

	output.open("p1.in");
	if(output.fail())
		cerr << "Error al abrir archivo de salida" << endl;

	srand((unsigned)time(0));

	// Por cada instancia que quiero crear...
	for (int i = 0; i < 100; i++){

		// Pongo la cantidad de máquinas
		int n = rand() % 1000000;
		output << n << endl;
	
		// Pongo los tiempos de carga
		for (int i = 0; i < n; i++)
			output << rand() % 1000 << " ";
		
		output << endl;
		
		// Pongo los tiempos de producción
		for (int i = 0; i < n; i++)
			output << rand() % 1000 << " ";

		output << endl;	
	}
	
	output << '#';
	output.close();
}

vector<triplet> cargar(istringstream& is){
	string l1;
	string l2;
	string l3;
	getline(is,l1);
	getline(is,l2);
	getline(is,l3);
	int cant;
	cant = atoi(l1.c_str());
	vector<triplet> p(cant); // <i,c,p>
	istringstream ssc(l2);
	istringstream ssp(l3);
	for(int i=0; i < cant; i++){
		p[i].numero = i+1;
		ssc >> p[i].carga;
		ssp >> p[i].produccion;
	}
	return p;
}

void resolver(vector<triplet>& v){
	if(tiempo){
		uint64_t t;
		uint64_t res = 0;
		for(int i = 0; i < ITER; i++){
			vector<triplet> vcopy = v;
			t = rdtsc();
			sort(vcopy.begin(),vcopy.end());
			t = rdtsc() -t;
			res = res + t;
		}
		res = res/ITER;
		int n = v.size();
		performanceOutput << n << " " << res << endl;
		//cout << "For n=" << n << " process took: "<< res << " ticks" << endl;	
	}
	sort(v.begin(),v.end());
//	copy(v.begin(), v.end(), ostream_iterator<triplet>(cout, " "));
//	cout << endl;
}

void salida(ostream& out, vector<triplet>& p){
	int inicio = 0;
	int fin = 0;
	int finGlobal = 0;
	for(vector<triplet>::iterator it=p.begin(); it!=p.end(); it++){
		out << it->numero << " ";
		fin = inicio + it->carga + it->produccion;
		inicio = inicio + it->carga;
		if(finGlobal < fin){
			finGlobal = fin;
		}
	}
	out << finGlobal << endl;
}
